import os, sys, socket, re, urllib, zlib
from Cheetah.Template import Template
from plugin import Plugin, quote, unquote
from urlparse import urlparse
from xml.sax.saxutils import escape
from lrucache import LRUCache
from UserDict import DictMixin
from datetime import datetime, timedelta
import config
import time
import subprocess
import mind
import logging
import shutil
import xml
import urllib
from Cheetah.Filters import Filter

SCRIPTDIR = os.path.dirname(__file__)

CLASS_NAME = 'Youtube'

default = "http://gdata.youtube.com/feeds/api/standardfeeds/top_rated"

# Preload the templates
tcname = os.path.join(SCRIPTDIR, 'templates', 'container.tmpl')
ttname = os.path.join(SCRIPTDIR, 'templates', 'TvBus.tmpl')
txname = os.path.join(SCRIPTDIR, 'templates', 'container.xsl')
CONTAINER_TEMPLATE = file(tcname, 'rb').read()
TVBUS_TEMPLATE = file(ttname, 'rb').read()
XSL_TEMPLATE = file(txname, 'rb').read()

# XXX BIG HACK
# subprocess is broken for me on windows so super hack
def patchSubprocess():
    o = subprocess.Popen._make_inheritable

    def _make_inheritable(self, handle):
        if not handle: return subprocess.GetCurrentProcess()
        return o(self, handle)

    subprocess.Popen._make_inheritable = _make_inheritable

mswindows = (sys.platform == "win32")
if mswindows:
    patchSubprocess()

def kill(pid):
    if mswindows:
        win32kill(pid)
    else:
        import signal
        os.kill(pid, signal.SIGTERM)

def win32kill(pid):
    import ctypes
    handle = ctypes.windll.kernel32.OpenProcess(1, False, pid)
    ctypes.windll.kernel32.TerminateProcess(handle, -1)
    ctypes.windll.kernel32.CloseHandle(handle)

def qoramp(path):
    if path.find("?") == -1:
        return "?"
    else:
        return "&"

class Youtube(Plugin):
    CONTENT_TYPE = 'x-container/tivo-videos'

    videos_feed = "http://gdata.youtube.com/feeds/api/videos"
    watch_video = "http://www.youtube.com/watch"
    get_video = 'http://www.youtube.com/get_video'
    standardfeeds = "http://gdata.youtube.com/feeds/api/standardfeeds/%s"
    standardfeedsregion = "http://gdata.youtube.com/feeds/api/standardfeeds/%s/%s"
    user_feed = "http://gdata.youtube.com/feeds/api/users/%s/%s"
    playlist = "http://gdata.youtube.com/feeds/api/playlists/%s"

    videos = {}

    #def __init__(self):
    #    try:
    #        from multiprocessing import Process, Lock
    #        p = Process(target=self.LoadVideos)
    #        p.start()
    #        p.join()
    #    except(ImportError):
    #        return self
    #   return self
    
    def __init__(self):
        #logging.debug('Starting Youtube Plugin Cache')
        #self.QueryContainer()
        return

    #def LoadVideos(self):
    #    while 1:
    #        time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime())
    #        for k,v in videos.iteritems():

    def send_file(self, handler, container, name):
        if handler.headers.getheader('Range') and \
           handler.headers.getheader('Range') != 'bytes=0-':
            handler.send_response(206)
            handler.send_header('Connection', 'close')
            handler.send_header('Content-Type', 'video/x-tivo-mpeg')
            handler.send_header('Transfer-Encoding', 'chunked')
            handler.end_headers()
            handler.wfile.write("\x30\x0D\x0A")
            return

        def between(data, From, To):
            start = data.find(From)
            if start == -1:
                return ''
            start += len(From)
            end = data.find(To, start)
            if end == -1:
                return ''
            return data[start:end]

        page = urllib.urlopen(self.watch_video + "?v=%s" % handler.path.split("/")[2]).read()
        ticket = between(page, '"t": "', '"')
        if not ticket:
            handler.send_error(404)
            return None
        
        url = self.get_video + "?video_id=%s&t=%s" % (handler.path.split("/")[2], ticket)

	#if container.has_key("fmt"):
        #    url = url + "&fmt=%s" % (config.get(container, "fmt"))

        logging.debug('download url is %s' % url)
        handler.send_response(200)
        handler.end_headers()

        settings = '-vcodec mpeg2video -r 29.97 -b 4096K -maxrate 17408k -bufsize 1024k -aspect 4:3 -s 544x480 -ab 192k -ar 48000 -acodec mp2 -ac 2 -f vob -'
        if mswindows:
            url = urllib.urlopen(url).geturl()
            cmd = [config.get('Server', 'ffmpeg'), '-i', url] + settings.split(' ')
            logging.debug('transcoding using ffmpeg command:')
            logging.debug(' '.join(cmd))
            ffmpeg = subprocess.Popen(cmd, bufsize=(512 * 1024), stdout=subprocess.PIPE)
        else:
            cmd = [config.get('Server', 'ffmpeg'), '-i', '-'] + settings.split(' ')
            source = urllib.urlopen(url)
            logging.debug('transcoding using ffmpeg command:')
            logging.debug(' '.join(cmd))
            ffmpeg = subprocess.Popen(cmd, bufsize=(512 * 1024), stdout=subprocess.PIPE, stdin=source)            
        
        try:
            shutil.copyfileobj(ffmpeg.stdout, handler.wfile)
        except:
            kill(ffmpeg.pid)

        source.close()

    def __duration(self, full_path):
        return transcode.video_info(full_path)['millisecs']

    def __est_size(self, full_path, tsn = ''):
        return int(full_path.duration) * 54672

    def getVideo(self, entry):
        video = {}
        for contentlink in entry.getElementsByTagName("media:content"):
            video['format'] = contentlink.attributes["yt:format"].value
            video[video['format']] = contentlink.attributes["url"].value
            video['type'] = contentlink.attributes["type"].value # always application/x-shockwave-flash ?
            video['medium'] = contentlink.attributes["medium"].value
            video['duration'] = contentlink.attributes["duration"].value

        nodes = ['summary', 'id', 'published', 'title', 'updated', 'yt:aboutMe', 'yt:age', 'yt:books', 'yt:company', 'yt:countHint', 'yt:firstName', 'yt:gender', 'yt:hobbies', 'yt:hometown', 'yt:lastName', 'yt:location', 'yt:movies', 'yt:music', 'yt:noembed', 'yt:occupation', 'yt:playlistId', 'yt:playlistTitle', 'yt:position', 'yt:private', 'yt:queryString', 'yt:recorded', 'yt:relationship', 'yt:school', 'yt:uploaded', 'yt:username', 'yt:status', 'yt:videoid', 'media:category', 'media:description', 'media:keywords', 'media:rating', 'media:restriction', 'media:title', 'gml:pos', 'app:edited']
        for nodename in nodes:
            try:
                node = entry.getElementsByTagName(nodename)[0]
                if nodename.find(":")!=-1:
                    nodename = nodename.split(":")[1]
                if node.nodeType == node.ELEMENT_NODE:
                    video[nodename] = node.firstChild.data
                elif node.nodeType == node.TEXT_NODE:
                    video[nodename] = node.data
            except(IndexError, AttributeError):
                if nodename.find(":")!=-1:
                    nodename = nodename.split(":")[1]
                video[nodename] = False

        if entry.getElementsByTagName("yt:state"):
            video['playable'] = False
        else:
            video['playable'] = True

        if video['playlistId']:
            video['id'] = video['playlistId']
            video['group'] = True
            video['title'] = self.getTitle(entry)
            url = self.playlist % video['id']
            feed = xml.dom.minidom.parseString(urllib.urlopen(url).read())
            video['type'] = "playlist"
            video['isdir'] = True
            logging.debug('Getting playlist %s at %s' % (video['title'], url))
            self.videos.update({video['id']: self.getVideos(feed)})
            video['total_items'] = len(self.videos[video['id']])
        elif video['videoid']:
            video['id'] = video['videoid']
            if entry.getElementsByTagName('gd:rating'):
                rating = entry.getElementsByTagName("gd:rating")[0]
                video['rating'] = rating.getAttribute('average')
            else:
                video['rating'] = '3'
            video['type'] = "video"
            video['isdir'] = False
            video['duration'] = entry.getElementsByTagName("yt:duration")[0].attributes["seconds"].value
            if video['keywords']:
                video['vProgramGenre'] = video['keywords'].split(", ")
            video['channelnumber'] = "0" # maybe have different channels for each youtube channel?
            video['channelname'] = "YOUTUBE" # maybe have different names for each youtube channel?
            video['showingBits'] = "1" # change this for different youtube ratings
            video['displayMinorNumber'] = "111"
            video['episodeTitle'] = video['title']
            video['seriesTitle'] = video['title']
            video['isEpisode'] = 'false'
            video['vChoreographer'] = []
            #author = entry.getElementsByTagName("author")[0]
            #video['author'] = author.getElementsByTagName("name")[0].firstChild.data
            #video['author.uri'] = author.getElementsByTagName("uri")[0].firstChild.data
            video['author'] = entry.getElementsByTagName("media:credit")[0].firstChild.data
            video['vActor'] = [video['author']]
            video['vExecProducer'] = []
            video['vGuestStar'] = []
            video['vSeriesGenre'] = []
            video['vHost'] = []
            video['vWriter'] = []
            video['vProducer'] = []
            video['showType'] = ('SERIES', '5')
            video['tvRating'] = "5"
            if video['rating']:
                video['starRating'] = str(round(float(video['rating']) * 7 / 5)).split(".")[0]
            else:
                video['starRating'] = '4'
            video['vDirector'] = []
            video['mpaaRating'] = "N8"
            video['episodeNumber'] = '1'
            video['seriesId'] = ""
    
            duration_delta = timedelta(milliseconds=int(video['duration'])*1000)
    
            try:
                from xml.utils import iso8601
                date = iso8601.parse(video['uploaded'].strip())
                utcdate = datetime.utcfromtimestamp(date)
                video['time'] = utcdate.isoformat()
                video['startTime'] = utcdate.isoformat()
                video['stopTime'] = utcdate+duration_delta
            except(ImportError):
                now = datetime.utcnow()
                video['time'] = now.isoformat()
                video['startTime'] = now.isoformat()
                video['stopTime'] = (now + duration_delta).isoformat()
    
            #video['originalAirDate'] = video['uploaded'].replace(".000Z", "")
            video['movieYear'] = video['uploaded'].split("T")[0].split("-")[0]
    
            min = duration_delta.seconds / 60
            sec = duration_delta.seconds % 60
            hours = min / 60
            min = min % 60
            video['iso_duration'] = 'P' + str(duration_delta.days) + \
                                       'DT' + str(hours) + 'H' + str(min) + \
                                       'M' + str(sec) + 'S'
            video['size'] = int(video['duration']) * 546720
            video['milliseconds'] = int(video['duration']) * 1000
        elif video['username']:
            folder['group'] = True
            url = entry.getElementsByTagName("content")[0].getAttribute('src')
            feed = xml.dom.minidom.parseString(urllib.urlopen(url).read())
            video['title'] = self.getTitle(feed)
            video['id'] = video['username']
            video['type'] = "subscription"
            video['isdir'] = True
            self.videos[video['id']] = self.getVideos(feed)
            video['total_items'] = len(self.videos[video['id']])
        elif entry.getElementsByTagName("media:player"):
            video['id'] = entry.getElementsByTagName("media:player")[0].getAttribute('url').replace('http://www.youtube.com/watch?v=', '')
            rating = entry.getElementsByTagName("gd:rating")[0]
            video['rating'] = rating.getAttribute('average')
            video['type'] = "video"
            video['isdir'] = False
            video['duration'] = entry.getElementsByTagName("yt:duration")[0].attributes["seconds"].value
            video['vProgramGenre'] = video['keywords'].split(", ")
            video['channelnumber'] = "0" # maybe have different channels for each youtube channel?
            video['channelname'] = "YOUTUBE" # maybe have different names for each youtube channel?
            video['showingBits'] = "1" # change this for different youtube ratings
            video['displayMinorNumber'] = "111"
            video['episodeTitle'] = video['title']
            video['seriesTitle'] = video['title']
            video['isEpisode'] = 'false'
            video['vChoreographer'] = []
            author = entry.getElementsByTagName("author")[0]
            video['author'] = author.getElementsByTagName("name")[0].firstChild.data
            #video['author.uri'] = author.getElementsByTagName("uri")[0].firstChild.data
            video['vActor'] = [video['author']]
            video['vExecProducer'] = []
            video['vGuestStar'] = []
            video['vSeriesGenre'] = []
            video['vHost'] = []
            video['vWriter'] = []
            video['vProducer'] = []
            video['showType'] = ('SERIES', '5')
            video['tvRating'] = "5"
            if video['rating']:
                video['starRating'] = str(round(float(video['rating']) * 7 / 5)).split(".")[0]
            else:
                video['starRating'] = '4'
            video['vDirector'] = []
            video['mpaaRating'] = "N8"
            video['episodeNumber'] = '1'
            video['seriesId'] = ""
    
            duration_delta = timedelta(milliseconds=int(video['duration'])*1000)
    
            try:
                from xml.utils import iso8601
                date = iso8601.parse(video['uploaded'].strip())
                utcdate = datetime.utcfromtimestamp(date)
                video['time'] = date.isoformat()
                video['startTime'] = date.isoformat()
                video['stopTime'] = date+duration
            except(ImportError):
                now = datetime.utcnow()
                video['time'] = now.isoformat()
                video['startTime'] = now.isoformat()
                video['stopTime'] = (now + duration_delta).isoformat()
    
            #video['originalAirDate'] = video['uploaded'].replace(".000Z", "")
            video['movieYear'] = video['updated'].split("T")[0].split("-")[0]
    
            min = duration_delta.seconds / 60
            sec = duration_delta.seconds % 60
            hours = min / 60
            min = min % 60
            video['iso_duration'] = 'P' + str(duration_delta.days) + \
                                       'DT' + str(hours) + 'H' + str(min) + \
                                       'M' + str(sec) + 'S'
            video['size'] = int(video['duration']) * 546720
            video['milliseconds'] = int(video['duration']) * 1000
        else:
            logging.debug('The entry named %s probably does not have proper pyTiVo Youtube Plugin support' % video['title'])
            return False

        return video
   
    def getVideos(self, feed, offset=0):
        videos = []
        for entry in feed.getElementsByTagName("entry"):
            video = self.getVideo(entry)
            if video: videos.append(video)
        return videos

    def getTitle(self, feed):
        return feed.getElementsByTagName("title")[0].firstChild.data

    def item_count(self, query, files, last_start=0):
        """Return only the desired portion of the list, as specified by 
           ItemCount, AnchorItem and AnchorOffset. 'files' is either a 
           list of objects with an 'id' attribute.
        """
        totalFiles = len(files)
        index = 0

        if totalFiles and query.has_key('ItemCount'):
            count = int(query['ItemCount'][0])

            if query.has_key('AnchorItem'):
                anchor = query['AnchorItem'][0]
                anchor = unquote(anchor)
                anchor = anchor.replace("/%s/" % query['Container'][0].split("/")[0], "", 1)
                anchor = anchor.replace("/TiVoConnect?Command=QueryContainer&Container=%s&id=" % query['Container'][0],'',1)
                anchor = anchor.replace("/TiVoConnect?Command=QueryContainer&Container=%s/" % query['Container'][0],'',1)

                filenames = [x['id'] for x in files]

                try:
                    index = filenames.index(anchor)
                except ValueError:
                    logging.debug('Anchor not found: %s' % anchor)

                if count > 0:
                    index += 1

                if query.has_key('AnchorOffset'):
                    index += int(query['AnchorOffset'][0])

                #foward count
                if count >= 0:
                    files = files[index:index + count]
                #backwards count
                else:
                    if index + count < 0:
                        count = -index
                    files = files[index + count:index]
                    index += count

            else:  # No AnchorItem

                if count >= 0:
                    files = files[:count]
                else:
                    index = count % len(files)
                    files = files[count:]

        return files, index

    def QueryContainer(self, handler, query):
        tsn = handler.headers.getheader('tsn', '')
        subcname = query['Container'][0]
        cname = subcname.split('/')[0]
        container = handler.server.containers[cname]

        try:
            id = query['id'][0]
        except(KeyError):
            id = subcname

        if subcname.find("/")!=-1:
            id = id.split("/")[1]
            try:
                videos = self.videos[id]
            except(KeyError):
                path = self.playlist % id
                logging.debug('Getting: %s' % path)
                feed = xml.dom.minidom.parseString(urllib.urlopen(path).read())
                self.videos.update({id: self.getVideos(feed)})
                videos = self.videos[id]
            total = len(videos)
        else:
            if container.has_key('cache') and (container.get('cache').lower().startswith('y') or container.get('cache').lower().startswith('t')):
                videos = self.videos[id]
            else:
                path = ""
                if container.has_key('user') and not container.has_key('path') and not container.has_key('playlist'):
                    userfolders = ["uploads", "favorites", "playlists"]
                    videos = []
                    for folder in userfolders:
                        video = {}
                        path = "http://gdata.youtube.com/feeds/api/users/%s/%s" % (container.get('user'), folder)
                        try:
                            feed = xml.dom.minidom.parseString(urllib.urlopen(path).read())
                        except:
                            continue
                        self.videos.update({"%s/%s" % (subcname,folder): self.getVideos(feed)})
                        video['total_items'] = len(self.videos["%s/%s" % (subcname,folder)])
                        video['group'] = False
                        video['title'] = folder
                        video['id'] = "%s/%s" % (subcname,folder)
                        video['isdir'] = True
                        videos.append(video)
                else:
                    if container.has_key('path'):
                        if container.get('path').startswith("http://gdata.youtube.com/feeds/"):
                            path = container.get('path')
                        elif container.has_key("author"):
                            path = self.user_feed % (container.get('author'),container.get('path'))
                        elif container.has_key("user"):
                            path = self.user_feed % (container.get('user'),container.get('path'))
                        else:
                            path = container.get('path').replace(" ", "_")
                            path = path.lower()
                            if container.has_key("category"):
                                path = path + "_%s" % (container.get("category"))
                            if container.has_key("region"):
                                self.standardfeedsregion % (container.get("region").upper(), path)
                            else:
                                path = self.standardfeeds % path
                    elif container.has_key('playlist'):
                        path = self.playlist % container.get('playlist')

                    if container.has_key('search'):
                        path = path + "%sq=%s" % (qoramp(path), container.get('search'))
                        if path.startswith("?"):
                            path = self.videos_feed + path
                    if container.has_key('q'):
                        path = path + "%sq=%s" % (qoramp(path), container.get('q'))
                    if container.has_key('start-index'):
                        path = path + "%sstart-index=%s" % (qoramp(path), container.get('start-index'))
                    if container.has_key('max'):
                        path = path + "%smax-results=%s" % (qoramp(path), container.get('max'))
                    elif container.has_key('max-results'):
                        path = path + "%smax-results=%s" % (qoramp(path), container.get('max-results'))
                    if container.has_key('safeSearch'):
                        path = path + "%ssafeSearch=%s" % (qoramp(path), container.get('safeSearch'))
                    else:
                        path = path + "%ssafeSearch=%s" % (qoramp(path), "strict")
                    if container.has_key("time"):
                        path = path + "%stime=%s" % (qoramp(path), container.get('time'))
                    if container.has_key("uploader"):
                        path = path + "%suploader=%s" % (qoramp(path), container.get('uploader'))
                    if container.has_key("restriction"):
                        path = path + "%srestriction=%s" % (qoramp(path), container.get('restriction'))
                    if container.has_key("orderby"):
                        path = path + "%sorderby=%s" % (qoramp(path), container.get('orderby'))
                    if container.has_key("lr"):
                        path = path + "%slr=%s" % (qoramp(path), container.get('lr'))            
                    if container.has_key("location-radius"):
                        path = path + "%slocation-radius=%s" % (qoramp(path), container.get('location-radius'))
                    if container.has_key("location"):
                        path = path + "%slocation=%s" % (qoramp(path), container.get('location'))
                    if container.has_key("format"):
                        path = path + "%sformat=%s" % (qoramp(path), container.get('format'))
                    if container.has_key("client"):
                        path = path + "%sclient=%s" % (qoramp(path), container.get('client'))
                    if container.has_key("category"):
                        path = path + "%scategory=%s" % (qoramp(path), container.get('category'))
                    if container.has_key('v'):
                        path = path + "%sv=%s" % (qoramp(path), container.get('v'))
                    elif container.has_key('version'):
                        path = path + "%sv=%s" % (qoramp(path), container.get('version'))
                    else:
                        path = path + "%sv=2" % (qoramp(path))
                    if query.has_key('ItemCount') and query.has_key('AnchorItem'):
                        anchor = query['AnchorItem'][0]
                        anchor = unquote(anchor)
                        anchor = anchor.replace("/%s/" % query['Container'][0].split("/")[0], "", 1)
                        anchor = anchor.replace("/TiVoConnect?Command=QueryContainer&Container=%s&id=" % query['Container'][0],'',1)
                        anchor = anchor.replace("/TiVoConnect?Command=QueryContainer&Container=%s/" % query['Container'][0],'',1)
                        filenames = [x['id'] for x in self.videos[id]]
                        try:
                            index = filenames.index(anchor)
                            if int(query['ItemCount'][0]) > 0:
				index += 1
                            offset=index
                            path = path + "%sstart-index=%s&max-results=%s" % (qoramp(path),str(index),query['ItemCount'][0])
                        except ValueError:
                            logging.debug('Anchor not found: %s' % anchor)
			    offset=0
                    elif query.has_key('ItemCount'):
			if int(query['ItemCount'][0])<0:
			    feed = xml.dom.minidom.parseString(urllib2.open(path + "%smax-results=0" % (qoramp(path)).read()))
			    total = int(feed.getElementsByTagName("openSearch:totalResults")[0].firstChild.data)
			    path = path + "%sstart-index=%s&max-results=%s" % (qoramp(path), total + int(query['ItemCount'][0]), str(abs(int(query['ItemCount'][0]))))
			else:
	                        path = path + "%smax-results=%s" % (qoramp(path),query['ItemCount'][0])
                        offset=0
                    else:
                        offset=0

                    logging.debug('Getting: %s' % path)

                    feed = xml.dom.minidom.parseString(urllib.urlopen(path).read())

		    total = int(feed.getElementsByTagName("openSearch:totalResults")[0].firstChild.data)
		    self.maxresults = total


                    nvideos = self.getVideos(feed)
                    for video in range(len(nvideos)):
			try:
	                        self.videos[id].insert(offset+video,nvideos[video])
			except(KeyError):
				self.videos.update({id: []})
	                        self.videos[id].insert(offset+video,nvideos[video])

                    self.videos.update({id: nvideos})

            if container.has_key("refresh"):
                if container.get("refresh").lower() == "yes":
                    refresh = {}
                    refresh['total_items'] = 1
                    refresh['group'] = False
                    refresh['title'] = "Refresh"
                    refresh['id'] = "refresh"
                    refresh['isdir'] = True
                    videos.append(refresh)

        if container.has_key("group"):
            folders = []
            groups = []
            for video in videos:
                if video[container.get("group")] in groups:
                    self.videos["%s/%s" % (subcname,video[container.get("group")])].append(video)
                    for folder in folders:
                        if folder['id'] == "%s/%s" % (subcname,video[container.get("group")]):
                            folder['total_items'] = folder['total_items'] + 1
                else:
                    self.videos["%s/%s" % (subcname,video[container.get("group")])] = []
                    self.videos["%s/%s" % (subcname,video[container.get("group")])].append(video)
                    groups.append(video[container.get("group")])
                    folder = {}
                    folder['total_items'] = 1
                    folder['group'] = False
                    folder['title'] = video[container.get("group")]
                    folder['id'] = "%s/%s" % (subcname,video[container.get("group")])
                    folder['isdir'] = True
                    folders.append(folder)
            for folder in folders:
                if folder['total_items'] == 1:
                    folder.update(self.videos[folder['id']][0])
            videos, start = self.item_count(query, folders)
        else:
            videos, start = self.item_count(query, nvideos)

        t = Template(CONTAINER_TEMPLATE, filter=EncodeUnicode)
        t.container = cname
        t.name = subcname #self.getTitle(feed)
        t.total = total
        t.start = start
        t.videos = videos
        t.quote = quote
        t.escape = escape
        t.crc = zlib.crc32
        t.guid = config.getGUID()
        t.tivos = config.tivos
        t.tivo_names = config.tivo_names
        handler.send_response(200)
        handler.send_header('Content-Type', 'text/xml')
        handler.end_headers()
        handler.wfile.write(t)

    def TVBusQuery(self, handler, query):
        tsn = handler.headers.getheader('tsn', '')
        id = query['id'][0]
        search = "http://gdata.youtube.com/feeds/api/videos?q=%s&max-results=%s&v=2"

        try:
            found=False
            for video in self.videos[query['Container'][0]]:
                if video['id'] == id:
                    file_info = video
		    found=True
                    break
            if found:
                file_info = self.getVideo(xml.dom.minidom.parseString(urllib.urlopen(search % (id,"1")).read()))
            else:
                file_info = self.getVideo(xml.dom.minidom.parseString(urllib.urlopen(search % (id,"1")).read()))
        except(KeyError):
            file_info = self.getVideo(xml.dom.minidom.parseString(urllib.urlopen(search % (id,"1")).read()))

        handler.send_response(200)
        handler.end_headers()
        t = Template(TVBUS_TEMPLATE, filter=EncodeUnicode)
        t.video = file_info
        t.escape = escape
        handler.wfile.write(t)

    def XSL(self, handler, query):
        handler.send_response(200)
        handler.end_headers()
        handler.wfile.write(XSL_TEMPLATE)

    def Push(self, handler, query):
        id = unquote(query['id'][0])

        search = "http://gdata.youtube.com/feeds/api/videos?q=%s&max-results=%s&v=2"

        tsn = query['tsn'][0]
        for key in handler.tivo_names:
            if handler.tivo_names[key] == tsn:
                tsn = key
                break

        try:
            found = False
            for video in self.videos[query['Container'][0]]:
                if video['id'] == id:
                    file_info = video
                    found = True
                    break
            if not found:
                file_info = self.getVideo(xml.dom.minidom.parseString(urllib.urlopen(search % (id,"1")).read()))
        except(KeyError):
            file_info = self.getVideo(xml.dom.minidom.parseString(urllib.urlopen(search % (id,"1")).read()))

        import socket
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(('tivo.com',123))
        ip = s.getsockname()[0]
        container = quote(query['Container'][0].split('/')[0])
        port = config.getPort()

        url = 'http://%s:%s/%s/%s' % (ip, port, container, quote(id))

        try:
            m = mind.getMind()
            m.pushVideo(
                tsn = tsn,
                url = url,
                description = file_info['description'],
                duration = int(file_info['duration']) / 1000,
                size = file_info['size'],
                title = file_info['title'],
                subtitle = file_info['title'])
        except Exception, e:
            import traceback
            handler.send_response(500)
            handler.end_headers()
            handler.wfile.write('%s\n\n%s' % (e, traceback.format_exc() ))
            raise

        referer = handler.headers.getheader('Referer')
        handler.send_response(302)
        handler.send_header('Location', referer)
        handler.end_headers()

class EncodeUnicode(Filter):
    def filter(self, val, **kw):
        """Encode Unicode strings, by default in UTF-8"""

        if kw.has_key('encoding'):
            encoding = kw['encoding']
        else:
            encoding='utf8'

        if type(val) == type(u''):
            filtered = val.encode(encoding)
        else:
            filtered = str(val)
        return filtered

class VideoDetails(DictMixin):

    def __init__(self, d=None):
        if d:
            self.d = d
        else:
            self.d = {}

    def __getitem__(self, key):
        if key not in self.d:
            self.d[key] = self.default(key)
        return self.d[key]

    def __contains__(self, key):
        return True

    def __setitem__(self, key, value):
        self.d[key] = value

    def __delitem__(self):
        del self.d[key]

    def keys(self):
        return self.d.keys()

    def __iter__(self):
        return self.d.__iter__()

    def iteritems(self):
        return self.d.iteritems()

    def default(self, key):
        defaults = {
            'showingBits' : '0',
            'episodeNumber' : '0',
            'displayMajorNumber' : '0',
            'displayMinorNumber' : '0',
            'isEpisode' : 'true',
            'colorCode' : ('COLOR', '4'),
            'showType' : ('SERIES', '5'),
            'tvRating' : ('NR', '7')
        }
        if key in defaults:
            return defaults[key]
        elif key.startswith('v'):
            return []
        else:
            return ''
